(function( $, undefined ) {
	$.widget = function(name, base, prototype) {
		var namespace = name.split('.')[0], full_name;
		name = name.split('.')[1];
		full_name = namespace + '-' + name;
		
		if (!prototype) {
			prototype = base;
			base = $.Widget;
		}
		
		$.expr[':'][full_name] = function(elem) {
			return !!$.data(elem, name);
		}
		
		$[namespace] = $[namespace] || {};
		$[namespace][name] = function(options, element) {
			if (arguments.length) {
				this.__create_widget(options, element);
			}
		};
		
		var base_prototype = new base();
		
		base_prototype.options = $.extend(true, {}, base_prototype.options);
		$[namespace][name].prototype = $.extend(true, base_prototype, {
			namespace: namespace,
			widget_name: name,
			widget_event_prefix: $[namespace][name].prototype.widget_event_prefix || name,
			widget_base_class: full_name
		}, prototype);
		
		$.widget.bridge(name, $[namespace][name]);
	};
	
	$.widget.bridge = function(name, object) {
		$.fn[name] = function(options) {
			// TODO 增加注释 Array.prototype.slice
			var is_method_call = typeof options === 'string',
				args = Array.prototype.slice.call(arguments, 1),
				return_val = this;
				
			// TODO 增加注释 $.extend.apply().concat()
			options = !is_method_call && args.length ? $.extend.apply(null, [true, options].concat(args)) : options;
			
			if (is_method_call && options.charAt(0) === '_') {
				return return_val;
			}
			
			if (is_method_call) {
				this.each(function() {
					var instance = $.data(this, name),
						method_val = instance && $.isFunction(instance[options]) ? // $.isFunction() 测试对象是否为函数
							instance[options].apply(instance, args) : instance;
							
					if (method_val !== instance && method_val !== undefined) {
						return_val = method_val;
						return false;
					}
				});
			} else {
				this.each(function()  {
					// $.data() 返回元素上储存的相应名字的数据，可以用data(name, value)来设定
					var instance = $.data(this, name);
					if (instance) {
						instance.option(options || {}).__init();
					} else {
						$.data(this, name, new object(options, this));
					}
				});
			}
			
			return return_val;
		};
	};
	
	$.Widget = function(options, element) {
		if (arguments.length) {
			this.__create_widget(options, element);
		}
	};
	
	$.Widget.prototype = {
		widget_name: 'widget',
		widget_event_prefix: '',
		options : {
			disabled: false
		},
		__create_widget: function(options, element) {
			$.data(element, this.widget_name, this);
			this.element = $(element);
			this.options = $.extend(true, {}, this.options, this.__get_create_options(), options);
			
			var self = this;
			this.element.bind('remove' + this.widget_name, function() {
				self.destroy();
			});
			
			this.__create();
			this.__trigger('create');
			this.__init();
		},
		__get_create_options: function() {
			return $.metadata && $.metadata.get(this.element[0])[this.widget_name];
		},
		__create: function() {},
		__init: function() {},
		destroy: function() {
			this.element
				.unbind('.' + this.widget_name)
				.removeData(this.widget_name);
			this.widget()
				.unbind('.' + this.widget_name)
				.removeAttr('aria-disabled')
				.removeClass(this.widget_base_class + '-disabled ' + 'mkui-state-disabled');
		},
		widget: function() {
			return this.element;
		},
		option: function(key, value) {
			var options = key;
			if (arguments.length == 0) {
				return $.extend({}, this.options);
			}
			if (typeof key === 'string') {
				if (value === undefined) {
					return this.options[key];
				}
				options = {};
				options[key] = value;
			}
			
			this.__set_options(options);
			return this;
		},
		__set_options: function(options) {
			var self = this;
			$.each(options, function(key, value) {
				self.__set_option(key, value);
			});
			
			return this;
		},
		__set_option: function(key, value) {
			this.options[key] = value;
			
			if (key === 'disabled') {
				this.widget()[value ? 'addClass' : 'removeClass']
					(this.widget_base_class + 'disabled' + ' ' + 'mkui-state-disabled')
					.attr('aria-disabled', value);
			}
			
			return this;
		},
		enable: function() {
			return this.__set_option('disabled', false);
		},
		disable: function() {
			return this.__set_option('disabled', true);
		},
		__trigger: function(type, event, data) {
			var prop, orig, 
				callback = this.options[type];
			
			data = data || {};
			// TODO $.Event() 
			event = $.Event(event);
			event.type = (type === this.widget_event_prefix ? type :
							this.widget_event_prefix + type).toLowerCase();
			event.target = this.element[0];
			// TODO event.originalEvent
			orig = event.originalEvent;
			if (orig) {
				for (prop in orig) {
					if (!(prop in event)) {
						event[prop] = orig[prop];
					}
				}
			}
			
			this.element.trigger(event, data);
			
			return !($.isFunction(callback) &&
					callback.call(this.element[0], event, data) === false ||
					event.isDefaultPrevented());
		}
	};
} )(jQuery);